package au.com.darkside.XServer.Xext;

import android.util.Log;
import android.graphics.Rect;
import android.graphics.Region;

import au.com.darkside.XServer.XServer;
import au.com.darkside.XServer.Client;
import au.com.darkside.XServer.InputOutput;
import au.com.darkside.XServer.Window;
import au.com.darkside.XServer.Util;
import au.com.darkside.XServer.Pixmap;
import au.com.darkside.XServer.ScreenView;
import java.util.*;

public class XShape
{
	private static int shapeMajor;
	private static int shapeMinor;
	
	public static void 
	processRequest(
	XServer xServer,	Client client,
	byte opcode, int arg,
	int bytesRemaining, int _sequenceNumber)
	throws java.io.IOException
	{
		InputOutput		io = client.getInputOutput ();
		int wID;
		Window window;
		switch(arg)
		{
			case Extensions.SHAPE_INIT:
				shapeMajor=1;
				shapeMinor=1;
				io.readSkip (bytesRemaining);
				synchronized(io)
				{
					Util.writeReplyHeader (client, arg);
					io.writeInt (0);	// Reply length.
					io.writeShort((short) shapeMajor);
					io.writeShort((short) shapeMinor);
					io.writePadBytes(20);
				}
				io.flush ();
				Log.i("XServExtensions","SHAPE - Major:"+Integer.toString(shapeMajor)+" Minor:"+Integer.toString(shapeMinor));
				break;
			case Extensions.SHAPE_MASK:
				int shapeOp=io.readByte();
				bytesRemaining-=1;
				int shapeKind=io.readByte();
				bytesRemaining-=1;
				io.readSkip(2);
				bytesRemaining-=2;
				wID=io.readInt();
				bytesRemaining-=4;
				int x=io.readShort();//need to translate to root coords
				bytesRemaining-=2;
				int y=io.readShort();//need to translate to root coords
				bytesRemaining-=2;
				int pixmap=io.readInt();
				bytesRemaining-=4;
				io.readSkip(bytesRemaining);
				/*Log.e("XServExtensions","shapeOp:"+Integer.toString(shapeOp));
				Log.e("XServExtensions","shapeKind:"+Integer.toString(shapeKind));
				Log.e("XServExtensions","window:"+Integer.toString(wID));
				Log.e("XServExtensions","x:"+Integer.toString(x));
				Log.e("XServExtensions","y:"+Integer.toString(y));
				Log.e("XServExtensions","pixmap:"+Integer.toString(pixmap));
				Log.e("XServExtensions","bytesRemaining:"+Integer.toString(bytesRemaining));*/
				window=(Window)xServer.getResource(wID);
				Rect or=window.getORect();
				x=x+or.left;
				y=y+or.top;
				/*Log.e("XServExtensions","x:"+Integer.toString(or.left));
				 Log.e("XServExtensions","y:"+Integer.toString(or.top));*/
				Rect createDefault;
				ShapeRegion destRgn;
				ShapeRegion srcRgn;
				switch(shapeKind)
				{
					case Extensions.SHAPE_BOUNDING:
						createDefault=window.getORect();
						destRgn=window.getBoundingShapeRegion();
						break;
					case Extensions.SHAPE_CLIP:
						createDefault=window.getIRect();
						destRgn=window.getClipShapeRegion();
						break;
					case Extensions.SHAPE_INPUT:
						createDefault=window.getORect();
						destRgn=window.getInputShapeRegion();
						break;
					default:
						createDefault=null;
						destRgn=null;
						Log.e("XServExtensions","unknown shape kind:"+Integer.toString(shapeKind));
						break;
				}
				Pixmap p=(Pixmap)xServer.getResource(pixmap);
				if(p==null)
					srcRgn=null;
				else{
					srcRgn=new ShapeRegion(p);
				}
				RegionOperate (client, window, shapeKind, destRgn, srcRgn, shapeOp, x, y, createDefault);
				//Log.e("XServExtensions","pixmap type"+Integer.toString(p.getType()));

				break;
			case Extensions.SHAPE_COMBINE:
			Log.e("shapCombine","*****");
				int shapeOpC=io.readByte();
				bytesRemaining-=1;
				int destKindC=io.readByte();
				bytesRemaining-=1;
				int sourceKindC=io.readByte();
				bytesRemaining-=1;
				io.readSkip(1);
				bytesRemaining-=1;
				int wIDC1=io.readInt();
				bytesRemaining-=4;
				int xC=io.readShort();
				bytesRemaining-=2;
				int yC=io.readShort();
				bytesRemaining-=2;
				int wIDC2=io.readInt();
				bytesRemaining-=4;

				Window destWindowC=(Window) xServer.getResource(wIDC1);
				Rect orC=destWindowC.getORect();
				/*Log.e("XServExtensions","x:"+Integer.toString(orC.left));
				 Log.e("XServExtensions","y:"+Integer.toString(orC.top));*/
				Window sourceWindowC=(Window) xServer.getResource(wIDC2);
				ShapeRegion sourceRegion=null;
				ShapeRegion destRegion=null;
				switch(destKindC)
				{
					case Extensions.SHAPE_BOUNDING:
						destRegion=destWindowC.getBoundingShapeRegion();
						Log.e("dest","SHAPEbounding");
						break;
					case Extensions.SHAPE_CLIP:
						destRegion=destWindowC.getClipShapeRegion();
						Log.e("dest","SHAPEclip");
						break;
					case Extensions.SHAPE_INPUT:
						destRegion=destWindowC.getInputShapeRegion();
						Log.e("dest","SHAPEinput");
						break;
				}
				switch(sourceKindC)
				{
					case Extensions.SHAPE_BOUNDING:
						sourceRegion=sourceWindowC.getBoundingShapeRegion();
						if(sourceRegion==null)
						{
							sourceRegion=new ShapeRegion();
							Rect orect=sourceWindowC.getORect();
							//orect.offset(orC.left,orC.top);
							sourceRegion._region.op(orect,Region.Op.REPLACE);
							Log.e("source","SHAPEbounding source is null");
						}
						Log.e("source","SHAPEbounding");
						break;
					case Extensions.SHAPE_CLIP:
						sourceRegion=sourceWindowC.getClipShapeRegion();
						Log.e("source","SHAPEclip");
						break;
					case Extensions.SHAPE_INPUT:
						sourceRegion=sourceWindowC.getInputShapeRegion();
						Log.e("source","SHAPEinput");
						break;
				}
				/*Log.e("x,y",Integer.toString(xC)+"-"+Integer.toString(yC));
				 Log.e("wDest,wSrc",Integer.toString(wIDC1)+"-"+Integer.toString(wIDC2));
				 Log.e("shapeOpC",Integer.toString(shapeOpC));*/
				RegionOperate (client, destWindowC, destKindC, destRegion, sourceRegion, shapeOpC, xC, yC, null);

				break;
			case Extensions.SHAPE_QUERY_EXTENT:
				wID=io.readInt();
				bytesRemaining-=4;
				io.readSkip(bytesRemaining);
				window=(Window)xServer.getResource(wID);
				byte bs=window.isBoundingShaped();
				byte cs=window.isClipShaped();
				Rect ors;
				if(bs==1)
					ors=window.getBoundingShapeRegion()._region.getBounds();
				else
					ors=window.getORect();
				Rect irs;
				if(cs==1)
					irs=window.getClipShapeRegion()._region.getBounds();
				else
					irs=window.getIRect();
				synchronized(io)
				{
					Util.writeReplyHeader(client,arg);
					io.writeInt(0);
					io.writeByte(bs);//bounding shaped?
					io.writeByte(cs);//clip shaped?
					io.writePadBytes(2);
					io.writeShort((short)ors.left);
					io.writeShort((short)ors.top);
					io.writeShort((short)ors.width());
					io.writeShort((short)ors.height());
					io.writeShort((short)irs.left);
					io.writeShort((short)irs.top);
					io.writeShort((short)irs.width());
					io.writeShort((short)irs.height());
					io.writePadBytes(4);
				}
				io.flush();
				break;
			case Extensions.SHAPE_SELECT_INPUT:
				wID=io.readInt();
				boolean enable=io.readByte()==1;
				io.readSkip(3);
				window=(Window)xServer.getResource(wID);
				window.setShapeSelectInput(enable,client);
				break;
			case Extensions.SHAPE_RECTANGLES:
				int shapeOpR=io.readByte();
				bytesRemaining-=1;
				int shapeKindR=io.readByte();
				bytesRemaining-=1;
				int shapeOrdering=io.readByte();
				bytesRemaining-=1;
				io.readSkip(1);
				bytesRemaining-=1;
				wID=io.readInt();
				bytesRemaining-=4;
				int xR=io.readShort();//need to translate to root coords
				bytesRemaining-=2;
				int yR=io.readShort();//need to translate to root coords
				bytesRemaining-=2;
				Vector<Rect> rects=new Vector<Rect>();
				Rect rect=new Rect();
				while(bytesRemaining>=8)
				{
					int xr=io.readShort();
					int yr=io.readShort();
					int wr=io.readShort();
					int hr=io.readShort();
					rect=new Rect();
					rect.set(xr,yr,xr+wr,yr+hr);
					rects.add(rect);
					bytesRemaining-=8;
					/*Log.e("XServExtensions","xr:"+Integer.toString(xr));
					Log.e("XServExtensions","yr:"+Integer.toString(yr));
					Log.e("XServExtensions","wr:"+Integer.toString(wr));
					Log.e("XServExtensions","hr:"+Integer.toString(hr));*/
					
				}
				io.readSkip(bytesRemaining);
				/*Log.e("XServExtensions","shapeOp:"+Integer.toString(shapeOpR));
				Log.e("XServExtensions","shapeKind:"+Integer.toString(shapeKindR));
				Log.e("XServExtensions","shapeOrdering:"+Integer.toString(shapeOrdering));
				Log.e("XServExtensions","window:"+Integer.toString(wID));
				Log.e("XServExtensions","x:"+Integer.toString(xR));
				Log.e("XServExtensions","y:"+Integer.toString(yR));
				Log.e("XServExtensions","rects:"+Integer.toString(rects.size()));
				Log.e("XServExtensions","bytesRemaining:"+Integer.toString(bytesRemaining));*/
				window=(Window)xServer.getResource(wID);
				Rect orR=window.getORect();
				xR=xR+orR.left;
				yR=yR+orR.top;
				Log.e("XServExtensions","x:"+Integer.toString(orR.left));
				Log.e("XServExtensions","y:"+Integer.toString(orR.top));
				Rect createDefaultR;
				ShapeRegion destRgnR;
				ShapeRegion srcRgnR;
				switch(shapeKindR)
				{
					case Extensions.SHAPE_BOUNDING:
						createDefaultR=window.getORect();
						destRgnR=window.getBoundingShapeRegion();
						break;
					case Extensions.SHAPE_CLIP:
						createDefaultR=window.getIRect();
						destRgnR=window.getClipShapeRegion();
						break;
					case Extensions.SHAPE_INPUT:
						createDefaultR=window.getORect();
						destRgnR=window.getInputShapeRegion();
						break;
					default:
						createDefaultR=null;
						destRgnR=null;
						Log.e("XServExtensions","unknown shape kind:"+Integer.toString(shapeKindR));
						break;
				}
				srcRgnR=new ShapeRegion(rects);
				RegionOperate (client, window, shapeKindR, destRgnR, srcRgnR, shapeOpR, xR, yR, createDefaultR);
				//Log.e("XServExtensions","pixmap type"+Integer.toString(p.getType()));
				break;
			default:
				byte[] ba = new byte[bytesRemaining];
				io.readBytes(ba, 0, bytesRemaining);
				String str=new String(ba);
				Log.e("XServExtensions","SHAPE unhandled minor opcode:"+Integer.toString(arg));
				Log.e("XServExtensions","DATA:"+str);
				break;
		}
	}
	private static boolean RegionOperate
	(Client client, Window window,
	 int shapeKind, ShapeRegion destRgn,
	 ShapeRegion srcRgn, int shapeOp,
	 int x, int y, Rect createDefault)
	{

		Log.e("ShapeOp",""+Integer.toString(shapeOp));
		ScreenView pScreen = window.getScreen();
		if (srcRgn!=null && (x!=0 || y!=0))
		{
			srcRgn._region.translate(x+window.getIRect().left-srcRgn._region.getBounds().left,y+window.getIRect().top-srcRgn._region.getBounds().top);
			Log.e("ShapeOp",""+Integer.toString(x+window.getIRect().left-srcRgn._region.getBounds().left));
			Log.e("ShapeOp",""+Integer.toString(y+window.getIRect().top-srcRgn._region.getBounds().top));
			Log.e("REGION_OPERRATE","Translate");
		}
		if (window.getParent()==null)
		{
			Log.e("REGION_OPERRATE","is root");
			if (srcRgn!=null)
				srcRgn=null;
			return true;
		}
		/* May/30/2001: * The shape.PS specs say if src is None, existing shape is to be
		 * removed (and so the op-code has no meaning in such removal);
		 * see shape.PS, page 3, ShapeMask. */
		if (srcRgn == null)
		{
			Log.e("REGION_OPERRATE","source is null");
			if (destRgn != null)
			{
				Log.e("REGION_OPERRATE","dest not null");
				//REGION_DESTROY (pScreen, destRgn);
				switch(shapeKind)
				{
					case Extensions.SHAPE_BOUNDING:
						window.setBoundingShapeRegion(null);
						break;
					case Extensions.SHAPE_CLIP:
						window.setClipShapeRegion(null);
						break;
					case Extensions.SHAPE_INPUT:
						window.setInputShapeRegion(null);
						break;
				}
				//destRgn.delete();
				destRgn = null;
				/* go on to remove shape and generate ShapeNotify */
			} else
			/* May/30/2001:
			 * The target currently has no shape in effect, so nothing to
			 * do here. The specs say that ShapeNotify is generated whenever
			 * the client region is "modified"; since no modification is done
			 * here, we do not generate that event. The specs does not say
			 * "it is an error to request removal when there is no shape in
			 * effect", so we return good status. */
				return true;
		} else{
			Log.e("Shapeop","switch"+Integer.toString(shapeOp));
			switch (shapeOp)
			{
				case Extensions.SHAPE_SET:
					Log.e("ShapeSet","h");
					if (destRgn!=null){
						Log.e("ShapeSet","destRgn not null");
						destRgn=null;}
					break;
				case Extensions.SHAPE_UNION:
					if (destRgn!=null){
						srcRgn._region.op(destRgn._region,Region.Op.UNION);
						Log.e("ShapeUnion","destRgn not null");
					}
					else{
						srcRgn._region.op(window.getORect(),Region.Op.UNION);
						Log.e("ShapeUnion","destRgn is null");
					}
					break;
				case Extensions.SHAPE_INTERSECT:
					Log.e("ShapeINTERS","");
					if (destRgn!=null)
						srcRgn._region.op(destRgn._region,Region.Op.INTERSECT);
					break;
				case Extensions.SHAPE_SUBTRACT:
					Log.e("ShapeSUB","");
					if (destRgn==null){
						Region _destRgn = new Region(window.getORect());
						srcRgn._region.op(_destRgn,Region.Op.DIFFERENCE);
					}
					else
						srcRgn._region.op(destRgn._region,Region.Op.DIFFERENCE);
					break;
				case Extensions.SHAPE_INVERT:
					Log.e("ShapeINV","");
					if (destRgn==null){
						Region _destRgn = new Region(window.getORect());
						srcRgn._region.op(_destRgn,Region.Op.REVERSE_DIFFERENCE);
					}
					else{
						srcRgn._region.op(srcRgn._region,Region.Op.REVERSE_DIFFERENCE);
					}
					break;
				default:
					Log.e("ShapeDefault","h");
					//client->errorValue = op;
					return false;
			}
			}

		switch(shapeKind)
		{
			case Extensions.SHAPE_BOUNDING:
				window.setBoundingShapeRegion(srcRgn);
				Log.e("***","setBoundingRegion");
				break;
			case Extensions.SHAPE_CLIP:
				window.setClipShapeRegion(srcRgn);
				Log.e("***","setClipRegion");
				break;
			case Extensions.SHAPE_INPUT:
				window.setInputShapeRegion(srcRgn);
				break;
		}
		window.SendShapeNotify (shapeKind);
		return true;
	}
}
